package com.sarc.vision;

import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.imgproc.Imgproc;

import android.os.AsyncTask;
import android.os.Looper;
import android.util.Log;

public class VideoProcessor implements CvCameraViewListener{
	
	//Please refer to OpticalFlowProcessor.h
	public static final int OPTICAL_FLOW_ACTION_CALC_FLOW_RANSAC_NODRAW		=0x00;
	public static final int OPTICAL_FLOW_ACTION_CALC_FLOW_RANSAC			=0x01;
	public static final int OPTICAL_FLOW_ACTION_CALC_FLOW_ONLY 				=0x02;
	public static final int OPTICAL_FLOW_ACTION_FEATURES_ONLY				=0x03;
	
	public static final int OPTICAL_FLOW_ACTION_PLAY_AROUND					=0x0F;

	//TODO: merge later.
	public static final int OPTICAL_FLOW_ACTION_CALC_SIFT_FEATURES			=0x04;
	public static final int OPTICAL_FLOW_ACTION_CALC_FREAK_FEATURES			=0x05;

	public static final int OPTICAL_FLOW_FEATURES_FAST						=0x10;
	public static final int OPTICAL_FLOW_FEATURES_SIFT						=0x11;
	public static final int OPTICAL_FLOW_FEATURES_ORB						=0x12;

	public static final int OPTICAL_FLOW_DESCRIPTOR_DEFAULT					=0x20;
	public static final int OPTICAL_FLOW_DESCRIPTOR_FREAK					=0x21;
	
	private Mat processFrame;
	private Mat outputFrame;
	private Mat tempFrame;
	private int currentAction;
	
	private long lastTimeMillis;
	private double currentFrameRate;
	private double lowestFrameRate;
	private int frameSkipped;

	private boolean isCalculating;
	private boolean newFrameReady;
	
	private final String TAG = "SARC:VP";
		
	public VideoProcessor()
	{
		processFrame = null;
		outputFrame = null;
		tempFrame = null;
		currentAction = OPTICAL_FLOW_ACTION_CALC_FLOW_RANSAC;
				
		isCalculating = false;
		newFrameReady = false;
		frameSkipped = 0;
		lowestFrameRate = 60;
		
		
		/*
		try
		{
			System.loadLibrary("nativevision");
		}
		catch (UnsatisfiedLinkError err)
		{
			Log.d(TAG, "Could not load native vision library");
		}
		*/	
        
	}
	
	public void changeAction(int action)
	{
		currentAction = action;
	}
	
	@Override
	public void onCameraViewStarted(int width, int height) {
		// TODO Auto-generated method stub
		processFrame = new Mat(height, width, CvType.CV_8UC1);
		outputFrame = new Mat(height, width, CvType.CV_8UC4);
		tempFrame = new Mat(height, width, CvType.CV_8UC4);
		OFPInit(width, height);
	}

	@Override
	public void onCameraViewStopped() {
		// TODO Auto-generated method stub
		processFrame.release();
		outputFrame.release();
		tempFrame.release();
		OFPRelease();
	}

	@Override
	public Mat onCameraFrame(Mat inputFrame) {
		// TODO Auto-generated method stub
		if (isCalculating)
		{
			if (frameSkipped == 0) inputFrame.copyTo(outputFrame);
			frameSkipped++;
			if (newFrameReady)
			{
		       tempFrame.copyTo(outputFrame);
			}
			return outputFrame;
		}
		else
		{
			//Imgproc.cvtColor(processFrame, outputFrame, Imgproc.COLOR_GRAY2BGRA,4);	
			Imgproc.cvtColor(inputFrame, processFrame, Imgproc.COLOR_BGRA2GRAY);
			inputFrame.copyTo(outputFrame);
			
			OFPProcess(currentAction, processFrame.getNativeObjAddr(), outputFrame.getNativeObjAddr());
			//new VisionTask().execute();
			
	        long currentTimeMillis = System.currentTimeMillis();
	        long diffTime = currentTimeMillis - lastTimeMillis;
	        currentFrameRate = 1f / (diffTime / 1000f);
	        if (currentFrameRate < lowestFrameRate && lastTimeMillis != 0) lowestFrameRate = currentFrameRate;
	        lastTimeMillis = currentTimeMillis;
	        

			return outputFrame;
		}
	}
	
	public double GetFrameRate()
	{
		return currentFrameRate;
	}
	
	public int GetFrameSkipped()
	{
		return frameSkipped;
	}
	
	public double GetLowestFrameRate()
	{
		return lowestFrameRate;
	}
	
	/*
	private class VisionTask extends AsyncTask<Void, Void, Void>
	{
		@Override
		protected void onPreExecute()
		{
			isCalculating = true;
			Looper.prepare();
			Looper.loop();
		}
		
		@Override
		protected Void doInBackground(Void... input) {
			// TODO Auto-generated method stub
			
			OFPProcess(currentAction, processFrame.getNativeObjAddr(), tempFrame.getNativeObjAddr());
			//Looper.loop();
			return null;
		}
		
		@Override
		protected void onPostExecute(Void result)
		{
			isCalculating = false;
			newFrameReady = true;
		}
	}
	*/
	
	private native void OFPInit(int width, int height);
	private native boolean OFPInitialized();
	private native void OFPRelease();
	private native void OFPProcess(int action, long addrGray, long addrFrameToDraw);
	private native double OFPGetDisplacementX();
	private native double OFPGetDisplacementY();
	private native double OFPOrientation();

}
